package com.ztsoft.user.config.shiro;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ztsoft.user.config.jwt.JwtToken;
import com.ztsoft.user.mapper.user.*;
import com.ztsoft.user.pojo.entity.user.*;
import com.ztsoft.user.utils.JWTUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.*;

/**
 * 自定义Realm
 */
public class UserRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(UserRealm.class);

    @Resource
    private SysUserMapper userMapper;
    @Resource
    private SysUserRoleMapper userRoleMapper;
    @Resource
    private SysRoleMapper roleMapper;
    @Resource
    private SysRoleMenuMapper roleMenuMapper;
    @Resource
    private SysMenuMapper menuMapper;

    /**
     * 多重写一个support
     * 标识这个Realm是专门用来验证JwtToken，不负责验证其他的token（UsernamePasswordToken）
     * 必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 执行授权逻辑
     *
     * @param principalCollection 身份集合
     * @return 授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("【执行授权逻辑】");

        //获取授权账号信息
        String account = principalCollection.toString();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        //获取用户信息
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account", account);
        SysUser sysUser = userMapper.selectOne(queryWrapper);

        //获取用户与角色关联信息
        QueryWrapper<SysUserRole> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("user_id", sysUser.getUserId());
        List<SysUserRole> sysUserRoleList = userRoleMapper.selectList(queryWrapper1);

        //获取角色信息
        List<SysRole> roleList = new ArrayList<>();
        for (SysUserRole sysUserRole : sysUserRoleList) {
            SysRole sysRole = roleMapper.selectById(sysUserRole.getRoleId());
            roleList.add(sysRole);
        }

        //获取角色与菜单关联信息
        List<SysRoleMenu> roleMenuList = new ArrayList<>();
        for (SysRole sysRole : roleList) {
            QueryWrapper<SysRoleMenu> queryWrapper2 = new QueryWrapper<>();
            queryWrapper2.eq("role_id", sysRole.getRoleId());
            List<SysRoleMenu> sysRoleMenuList = roleMenuMapper.selectList(queryWrapper2);
            roleMenuList.addAll(sysRoleMenuList);
        }

        //获取菜单信息
        List<SysMenu> menuList = new ArrayList<>();
        for (SysRoleMenu sysRoleMenu : roleMenuList) {
            SysMenu menu = menuMapper.selectById(sysRoleMenu.getMenuId());
            menuList.add(menu);
        }

        Set<String> roleNameList = new HashSet<>();
        Set<String> permissionNameList = new HashSet<>();

        roleList.forEach(role -> roleNameList.add(role.getRoleName()));
        menuList.forEach((menu -> permissionNameList.add(menu.getPerms())));

        //给资源进行授权，权限为数据库查询当前登录用户的授权字符串
        authorizationInfo.setRoles(roleNameList);
        authorizationInfo.setStringPermissions(permissionNameList);

        return authorizationInfo;
    }

    /**
     * 执行认证逻辑
     *
     * @param authenticationToken 用户提交的身份（如用户名）及凭据（如密码）
     * @return 认证信息
     * @throws AuthenticationException 认证异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("【执行认证逻辑】");

        String token = (String) authenticationToken.getCredentials();
        String account = null;
        try {
            //1.判断用户名
            account = JWTUtils.getTokenInfo(token, "account");

            QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_account", account);

            if (Objects.isNull(userMapper.selectOne(queryWrapper))) {
                //用户名不存在
                logger.info("【用户不存在】");
                //shiro底层会抛出UnknownAccountException
                return null;
            }
            //校验token
            if (!JWTUtils.verify(token)) {
                logger.info("【Token校验不正确】");
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //2.判断密码
        return new SimpleAuthenticationInfo(account, token, getName());
    }
}